﻿using System;
using Microsoft.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using Bling.DSL;
using Bling.Util;
using Bling.Core;
using System.Linq;
namespace Bling.Core {
  public abstract partial class Brand<T, BRAND> {
    public class SimpleArray {
      internal T[] Values;
      public BRAND this[IntBl Idx] {
        get { return ToBrand(new ArrayExpr<T>(Values, Idx));  }
      }
    }
    public static SimpleArray Array(int Size, Func<IntBl, BRAND> F) {
      var Values = new T[Size];
      for (int i = 0; i < Size; i++)
        Values[i] = F(i).CurrentValue;
      return new SimpleArray() { Values = Values };
    }
  }
}
namespace Bling.DSL {
  public abstract partial class Expr : IExpr {
    public abstract object BaseCurrentValue { get; }
    public abstract Func<object> BaseAsFunc { get; }
  }
  public abstract partial class Expr<T> : Expr, IExpr<T> {
    private Func<T> CurrentValue0;
    public virtual T CurrentValue { get { return AsFunc(); } }
    public Func<T> AsFunc {
      get {
        if (CurrentValue0 == null) {
          CurrentValue0 = (new Bling.Linq.DefaultLinqEval()).Eval<T>(this);
        }
        return CurrentValue0;
      }
    }
    public override Func<object> BaseAsFunc {
      get {
        var f = AsFunc;
        return () => f();
      }
    }
    public override object BaseCurrentValue { get { return CurrentValue; } }

  }
  public partial class Constant<T> : Expr<T>, IConstant {
    public override T CurrentValue { get { return Value; } }
  }


  public interface IArrayEval<EVAL> : IEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<T> Eval<T>(T[] Values, Eval<EVAL>.Info<int> Index);
  }
  public class ArrayExpr<T> : Expr<T>, ICanAssignExpr<T,Expression> {
    private readonly T[] Values;
    private readonly Expr<int> Index;
    public ArrayExpr(T[] Values, Expr<int> Index) {
      this.Values = Values; this.Index = Index;
    }
    protected override string ToString1() {
      return Values + "[" + Index + "]";
    }
    protected override int GetHashCode0() {
      return Values.GetHashCode() + Index.GetHashCode();
    }
    protected override bool Equals0(Expr<T> obj) {
      if (obj is ArrayExpr<T>)
        return Values.Equals(((ArrayExpr<T>)obj).Values) &&
                Index.Equals(((ArrayExpr<T>)obj).Index);
      return base.Equals0(obj);
    }
    protected override Eval<EVAL>.Info<T> Eval<EVAL>(Eval<EVAL> txt) {
      if (txt is IArrayEval<EVAL>) return ((IArrayEval<EVAL>)txt).Eval(Values, txt.DoEval(Index));
      throw new NotImplementedException();
    }
    public Expression Assign<EVAL>(Expression RHS0, IExpressionEval<EVAL, Expression> txt) where EVAL : Eval<EVAL> {
      return Expression.Assign(
        Expression.ArrayAccess(
          Expression.Constant(Values, typeof(T[])), txt.Extract(txt.DoEval(Index))), RHS0);
    }

    public Expression LinqAssign<EVAL>(Expression RHS0, Linq.LinqEval<EVAL> txt) where EVAL : Eval<EVAL> {
      return Expression.Assign(
        Expression.ArrayAccess(
          Expression.Constant(Values, typeof(T[])), txt.DoEval(Index).Value), RHS0);
    }
  }
}
namespace Bling.Core {
  using Bling.Linq;
  public partial interface IBrandT<T> {
    T CurrentValue { get; }
    Func<T> AsFunc { get; } 
  }
  public abstract partial class Brand<T, BRAND> : Brand<BRAND>, IBrandT<T>, IExtends<BRAND,ObjectBl> where BRAND : Brand<T, BRAND> {
    public T CurrentValue { get { return Provider.CurrentValue; } }
    public Func<T> AsFunc { get { return Provider.AsFunc; } }



    public static Func<T,S> Lambda<S,SBRAND>(BrandId<S,SBRAND> Id, Func<BRAND, SBRAND> F) where SBRAND : Brand<S,SBRAND> {
      var f= new DefaultLinqEval().Eval<T,S>(t => F(ToBrand(t)));
      return (t) => f(t);
    }
    public static Func<T, S> Lambda<S, SBRAND>(Func<BRAND, Brand<S, SBRAND>> F) where SBRAND : Brand<S, SBRAND> {
      var f = new DefaultLinqEval().Eval<T, S>(t => F(ToBrand(t)));
      return (t) => f(t);
    }
  }
  public static partial class BrandExtensions {
    //public static Func<T,S> F(this Brand

  }

}

namespace Bling.Ops {
  using linq = Microsoft.Linq.Expressions;
  using Bling.Linq;



  public partial interface IBaseOperator<S, TDelegate> : IOperator<S> {
    //TDelegate AsFunc { get; }
  }

  public partial interface IOperatorX<S, T> : IOperator<T> {
    Func<linq.Expression, linq.Expression> Linq(Operation<S, T> op);
  }
  public partial interface OperatorX<S, T, U> : IOperator<U> {
    Func<linq.Expression, linq.Expression, linq.Expression> Linq(Operation<S, T, U> op);
  }
  public partial interface OperatorX<S, T, U, V> : IOperator<V> {
    Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression> Linq(Operation<S, T, U, V> op);
  }
  public partial interface OperatorX<S, T, U, V, W> : IOperator<W> {
    Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression, linq.Expression> Linq(Operation<S, T, U, V, W> op);
  }
  public partial class BaseOperatorX<S, T> : BaseOperatorX<T>, IOperatorX<S, T> {
    public virtual Func<linq.Expression, linq.Expression> Linq(Operation<S, T> op) { 
      return null; 
    }
    private System.Func<S, T> AsFunc0;
    public System.Func<S, T> AsFunc {
      get {
        if (AsFunc0 != null) return AsFunc0;
        if (!CanOptimize) return null;
        var eval = new DefaultLinqEval();
        var AsFunc1 = eval.Eval<S, T>(s => Make(s));
        AsFunc0 = AsFunc1;
        return AsFunc0;
      }
    }
  }
  public partial class BaseOperatorX<S, T, U> {
    public virtual Func<linq.Expression, linq.Expression, linq.Expression> Linq(Operation<S, T, U> op) { return null; }
    private System.Func<S, T, U> AsFunc0;
    public System.Func<S, T, U> AsFunc {
      get {
        if (AsFunc0 != null) return AsFunc0;
        if (!CanOptimize) return null;
        var eval = new DefaultLinqEval();
        var AsFunc1 = eval.Eval<S, T, U>((s,t) => Make(s,t));
        AsFunc0 = AsFunc1;
        return AsFunc0;
      }
    }
  }
  public partial class BaseOperatorX<S, T, U, V> {
    public virtual Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression> Linq(Operation<S, T, U, V> op) { return null; }
    private System.Func<S, T, U,V> AsFunc0;
    public System.Func<S, T, U,V> AsFunc {
      get {
        if (AsFunc0 != null) return AsFunc0;
        if (!CanOptimize) return null;
        var eval = new DefaultLinqEval();
        var AsFunc1 = eval.Eval<S, T, U,V>((s, t,u) => Make(s, t,u));
        AsFunc0 = AsFunc1;
        return AsFunc0;
      }
    }
  }
  public partial class BaseOperatorX<S, T, U, V, W> {
    public virtual Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression, linq.Expression> Linq(Operation<S, T, U, V, W> op) { return null; }
    private Func<S, T, U, V, W> AsFunc0;
    public Func<S, T, U, V, W> AsFunc {
      get {
        if (AsFunc0 != null) return AsFunc0;
        var F = Linq(null);
        var paramS = linq.Expression.Parameter(typeof(S), "paramS");
        var paramT = linq.Expression.Parameter(typeof(T), "paramT");
        var paramU = linq.Expression.Parameter(typeof(U), "paramU");
        var paramV = linq.Expression.Parameter(typeof(V), "paramV");
        linq.Expression body;
        if (F == null) {
          var exprS = new Bling.Linq.LinqExpr<S>(paramS);
          var exprT = new Bling.Linq.LinqExpr<T>(paramT);
          var exprU = new Bling.Linq.LinqExpr<U>(paramU);
          var exprV = new Bling.Linq.LinqExpr<V>(paramV);
          var expand = Expand(exprS, exprT, exprU, exprV);
          if (expand == null) return null;
          // linq eval...
          AsFunc0 = (new Bling.Linq.DefaultLinqEval()).Eval<S, T, U, V, W>((exprS0, exprT0, exprU0, exprV0) => Expand(exprS0, exprT0, exprU0, exprV0));
        } else {
          body = F(paramS, paramT, paramU, paramV);
          var G = linq.Expression.Lambda<System.Func<S, T, U, V, W>>(body, paramS, paramT, paramU, paramV).Compile();
          AsFunc0 = (s, t, u, v) => G(s, t, u, v);
        }
        return AsFunc0;
      }
    }
  }
  public partial class MapOperator<S, T> : BaseOperatorX<S, T> {
    public override Func<linq.Expression, linq.Expression> Linq(Operation<S, T> op) {
      var f = linq.Expression.Constant(F, typeof(Func<S, T>));
      return s => linq.Expression.Invoke(f, s);
    }
  }
  public partial class MapOperator<S, T, U> : BaseOperatorX<S, T, U> {
    public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Operation<S, T, U> op) {
      var f = linq.Expression.Constant(STU, typeof(Func<S, T, U>));
      return (s, t) => linq.Expression.Invoke(f, s, t);
    }
  }
  public abstract partial class CastOperator<S, T, CNT> : Operator<S, T, CNT> {
    public override Func<linq.Expression, linq.Expression> Linq(Operation<S, T> op) {
      return e => linq.Expression.Convert(e, typeof(T));
    }
  }
  public partial class ToStringOperator : Operator<object, string, ToStringOperator> {
    private static readonly MethodInfo ToStringM = typeof(object).GetMethod("ToString");
    public override Func<linq.Expression, linq.Expression> Linq(Operation<object, string> op) {
      return e => linq.Expression.Call(e, ToStringM);
    }
  }
  /*
  public abstract partial class BaseStaticCallOperator<T, S, CNT> : Operator<T, S, CNT> {
    private bool IsLinqInit = false;
    private MethodInfo Method;
    public override Func<linq.Expression, linq.Expression> Linq(Operation<T, S> op) {
      if (!IsLinqInit) {
        Method = TargetType.GetMethod(CallName, new Type[] { typeof(T), });
        IsLinqInit = true;
      }
      if (Method == null) return base.Linq(op);
      else return e => linq.Expression.Call(Method, e);
    }
  }
  public abstract partial class StaticCallOperator<T, S, U, CNT> : Operator<T, S, U, CNT> {
    private bool IsLinqInit = false;
    private MethodInfo Method;

    public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Operation<T, S, U> op) {
      if (!IsLinqInit) {
        Method = TargetType.GetMethod(CallName, new Type[] { typeof(T), typeof(S) });
        IsLinqInit = true;
      }
      if (Method == null) return base.Linq(op);
      else return (e0, e1) => linq.Expression.Call(Method, e0, e1);
    }
  }
  public abstract partial class StaticCallOperator<T, S, U, V, CNT> : Operator<T, S, U, V, CNT> {
    private bool IsLinqInit = false;
    private MethodInfo Method;
    public override Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression> Linq(Operation<T, S, U, V> op) {
      if (!IsLinqInit) {
        Method = TargetType.GetMethod(CallName, new Type[] { typeof(T), typeof(S), typeof(U) });
        IsLinqInit = true;
      }
      if (Method == null) return base.Linq(op);
      else return (e0, e1, e2) => linq.Expression.Call(TargetType, CallName, new Type[] { }, e0, e1, e2);
    }
  }*/
  public partial class ExplicitConvertOperator<S, T> : CastOperator<S, T, ExplicitConvertOperator<S, T>> {
    public override Func<linq.Expression, linq.Expression> Linq(Operation<S, T> op) {
      if (ConvertF == null) return base.Linq(op);
      else return e => linq.Expression.Convert(e, typeof(T), ConvertF);
    }
  }
  public partial class Operation<S, T> : BaseOperation<T, IOperatorX<S, T>> {
    internal object AsFuncLinq0 {
      get {
        if (GCache.ContainsKey(Op)) return GCache[Op];
        Delegate G;
        var F = Op.Linq(this);
        var txt = new Linq.DefaultLinqEval();
        if (F != null) {
          var paramS = linq.Expression.Parameter(txt.TransformType(typeof(S)), "paramS");
          var body = F(paramS);
          txt.Convert(body, txt.TransformType(typeof(T)));
          G = linq.Expression.Lambda(body, paramS).Compile();
        } else {
          Expr<T> ex;
          var G0 = new Linq.DefaultLinqEval().Eval00<S, T>((s) => { ex = Op.Expand(s); return ex; });
          G = G0.Compile();
        }
        GCache[Op] = G;
        return G;
      }
    }
    internal System.Func<S, T> AsFuncLinq {
      get {
        if (FCache.ContainsKey(Op)) return (System.Func<S, T>)FCache[Op];
        var g = AsFuncLinq0;
        if (g.GetType() == typeof(System.Func<S, T>)) {
          FCache[Op] = (Delegate)g;
          return (System.Func<S, T>)g;
        } else {
          var G0 = Expression.Constant(g);
          var txt = new Linq.DefaultLinqEval();
          var G1 = (Expression<System.Func<S, T>>)txt.Convert(G0, typeof(System.Func<S, T>));
          var G = G1.Compile();
          FCache[Op] = (Delegate)G;
          return G;
        }
      }
    }
    
    
    public override Expr<T> Transform(Transformer t) {
      var ArgA = t.Transform(this.ArgA);
      if (ArgA is Constant<S>) {
        System.Func<S, T> G = AsFuncLinq;
        if (G != null)
          return new Constant<T>(G(((Constant<S>)ArgA).Value));
        Expr<T> e = Op.Expand(ArgA);
        if (e != null) return t.Transform(e);
      }
      return new Operation<S, T>() { ArgA = ArgA, Op = Op, };
    }
  }
  public partial class Operation<S, T, U> : BaseOperation<U, OperatorX<S, T, U>> {

    internal object AsFuncLinq0 {
      get {
        if (GCache.ContainsKey(Op)) return GCache[Op];
        Delegate G;
        var F = Op.Linq(this);
        var txt = new Linq.DefaultLinqEval();
        if (F != null) {
          var paramS = linq.Expression.Parameter(txt.TransformType(typeof(S)), "paramS");
          var paramT = linq.Expression.Parameter(txt.TransformType(typeof(T)), "paramT");
          var body = F(paramS, paramT);
          txt.Convert(body, txt.TransformType(typeof(U)));
          G = linq.Expression.Lambda(body, paramS, paramT).Compile();
        } else {
          var G0 = new Linq.DefaultLinqEval().Eval00<S, T, U>((s, t) => Op.Expand(s, t));
          G = G0.Compile();
        }
        GCache[Op] = G;
        return G;
      }
    }


    internal System.Func<S, T, U> AsFuncLinq {
      get {
        if (FCache.ContainsKey(Op)) return (System.Func<S, T, U>)FCache[Op];
        var g = AsFuncLinq0;
        if (g.GetType() == typeof(System.Func<S, T, U>)) {
          FCache[Op] = (Delegate)g;
          return (System.Func<S, T, U>)g;
        } else {
          var G0 = Expression.Constant(g);
          var txt = new Linq.DefaultLinqEval();
          var G1 = (Expression<System.Func<S, T, U>>)txt.Convert(G0, typeof(System.Func<S, T, U>));
          var G = G1.Compile();
          FCache[Op] = (Delegate)G;
          return G;
        }
      }
    }
    public override Expr<U> Transform(Transformer t) {
      var ArgA = t.Transform(this.ArgA);
      var ArgB = t.Transform(this.ArgB);
      if (ArgA is Constant<S> && ArgB is Constant<T>) {
        System.Func<S, T, U> G = AsFuncLinq;
        if (G != null)
          return new Constant<U>(G(((Constant<S>)ArgA).Value, ((Constant<T>)ArgB).Value));
        var e = Op.Expand(ArgA, ArgB);
        if (e != null) return t.Transform(e);
      }
      return new Operation<S, T, U>() { ArgA = ArgA, ArgB = ArgB, Op = Op };
    }
  }
  public partial class Operation<S, T, U, V> : BaseOperation<V, OperatorX<S, T, U, V>> {
    internal System.Func<S, T, U, V> AsFuncLinq {
      get {
        if (FCache.ContainsKey(Op)) return (System.Func<S, T, U, V>)FCache[Op];
        var F = Op.Linq(this);
        if (F == null) return null;
        var paramS = linq.Expression.Parameter(typeof(S), "paramS");
        var paramT = linq.Expression.Parameter(typeof(T), "paramT");
        var paramU = linq.Expression.Parameter(typeof(U), "paramU");
        var body = F(paramS, paramT, paramU);
        var G = linq.Expression.Lambda<System.Func<S, T, U, V>>(body, paramS, paramT, paramU).Compile();
        FCache[Op] = G;
        return G;
      }
    }
    public override Expr<V> Transform(Transformer t) {
      var ArgA = t.Transform(this.ArgA);
      var ArgB = t.Transform(this.ArgB);
      var ArgC = t.Transform(this.ArgC);
      if (ArgA is Constant<S> && ArgB is Constant<T> && ArgC is Constant<U>) {
        System.Func<S, T, U, V> G = AsFuncLinq;
        if (G != null)
          return new Constant<V>(G(((Constant<S>)ArgA).Value, ((Constant<T>)ArgB).Value, ((Constant<U>)ArgC).Value));
        var e = Op.Expand(ArgA, ArgB, ArgC);
        if (e != null) return t.Transform(e);
      }
      return new Operation<S, T, U, V>() { ArgA = ArgA, ArgB = ArgB, ArgC = ArgC, Op = Op };
    }
  }
  public partial class Operation<S, T, U, V, W> : BaseOperation<W, OperatorX<S, T, U, V, W>> {
    internal System.Func<S, T, U, V, W> AsFuncLinq {
      get {
        if (FCache.ContainsKey(Op)) return (System.Func<S, T, U, V, W>)FCache[Op];
        var F = Op.Linq(this);
        if (F == null) return null;
        var paramS = linq.Expression.Parameter(typeof(S), "paramS");
        var paramT = linq.Expression.Parameter(typeof(T), "paramT");
        var paramU = linq.Expression.Parameter(typeof(U), "paramU");
        var paramV = linq.Expression.Parameter(typeof(V), "paramV");
        var body = F(paramS, paramT, paramU, paramV);
        var G = linq.Expression.Lambda<System.Func<S, T, U, V, W>>(body, paramS, paramT, paramU, paramV).Compile();
        FCache[Op] = G;
        return G;
      }
    }
    public override Expr<W> Transform(Transformer t) {
      var ArgA = t.Transform(this.ArgA);
      var ArgB = t.Transform(this.ArgB);
      var ArgC = t.Transform(this.ArgC);
      var ArgD = t.Transform(this.ArgD);
      if (ArgA is Constant<S> && ArgB is Constant<T> && ArgC is Constant<U> && ArgD is Constant<V>) {
        System.Func<S, T, U, V, W> G = AsFuncLinq;
        if (G != null)
          return new Constant<W>(G(((Constant<S>)ArgA).Value, ((Constant<T>)ArgB).Value, ((Constant<U>)ArgC).Value, ((Constant<V>)ArgD).Value));

        var e = Op.Expand(ArgA, ArgB, ArgC, ArgD);
        if (e != null) return t.Transform(e);
      }
      return new Operation<S, T, U, V, W>() { ArgA = ArgA, ArgB = ArgB, ArgC = ArgC, ArgD = ArgD, Op = Op };
    }
  }
}

namespace Bling.Linq {
  using Bling.Vecs;
  using Bling.Ops;

  public static partial class LinqExtensions {
    public static Func<Expr<int>, ICanAssignExpr<T,Expression>> Raise<T>(this T[] Values) {
      return idx => new ArrayExpr<T>(Values, idx);
    }
  }
  public class ParameterContext {
    private readonly Func<Type, Type> TransformType;
    public ParameterContext(Func<Type,Type> TransformType) {
      this.TransformType = TransformType;
    }
    public readonly List<IParameterExpr> Slots = new List<IParameterExpr>();
    public readonly Dictionary<IParameterExpr, ParameterExpression> Expressions = new Dictionary<IParameterExpr, ParameterExpression>();
    public ParameterExpression this[IParameterExpr valueX] {
      get {
        if (!Expressions.ContainsKey(valueX)) {
          if (Slots.Contains(valueX)) {
            Expressions[valueX] = Expression.Parameter(TransformType(valueX.TypeOfT), valueX.Name);
          } else false.Assert();
        }
        return Expressions[valueX];
      }
      set {
        Expressions[valueX] = value;
      }
    }
    public ParameterExpression[] Flush() {
      var ret = new ParameterExpression[Slots.Count];
      for (int i = 0; i < Slots.Count; i++) {
        ret[i] = this[Slots[i]];
      }
      return ret;
    }

    public ParameterExpr<T> NewParameter<T>() {
      var ret = new ParameterExpr<T>();
      Slots.Add(ret);
      return ret;
    }
    public void Add<T>(ParameterExpr<T> arg) { Slots.Add(arg); }
    public void Add(IParameterExpr arg) { Slots.Add(arg); }
  }


  public class LinqExpr<T> : Expr<T> {
    public readonly Expression Expression;
    public LinqExpr(Expression Expression) { this.Expression = Expression; }
    protected override string ToString1() {
      return Expression.ToString();
    }
    protected  override Eval<EVAL>.Info<T> Eval<EVAL>(Eval<EVAL> txt) {
      if (txt is LinqEval<EVAL>) {
        var ret = new LinqEval<EVAL>.MyInfo<T>() { Value = Expression };
        return (Eval<EVAL>.Info<T>)(object)ret;
      } else if (txt is TranslateEval<EVAL>) {
        return ((TranslateEval<EVAL>)txt).NoTranslation(this);
      }
      throw new NotImplementedException();
    }
  }
  public abstract partial class LinqEval<EVAL> : ScopedEval<EVAL>, ITableEval<EVAL>, IParameterEval<EVAL>, Ops.IConditionEval<EVAL>, IArrayEval<EVAL>, ILoopEval<EVAL, Expression>, IExpressionEval<EVAL, Expression>, IVecEval<EVAL>, WPF.IWPFEval<EVAL>, Ops.IMapEval<EVAL> where EVAL : Eval<EVAL> {
    public ParameterContext Parameters { get; set; }
    public LinqEval() {
      Parameters = new ParameterContext(TransformType);
    }
    public IParameterExpr[] ExpressionsUsed { get { return Parameters.Expressions.Keys.ToArray(); } }

    public override Info<T> Parameter<T>(ParameterExpr<T> value) {
      var ret = base.Parameter(value);
      if (ret != null) return ret;
      return new MyInfo<T>() { Value = Parameters[value] };
    }
    public Info<T> Eval<T>(T[] Values, Info<int> Index) {
      return new MyInfo<T>() {
        Value = Expression.ArrayAccess(
          Expression.Constant(Values, TransformType(typeof(T[]))), ((MyInfo<int>)Index).Value),
      };
    }
    public TDelegate DoSetNow<T,TDelegate>(ICanAssignExpr<T,Expression> LHS, Expr<T> RHS, params IParameterExpr[] Args) {
      foreach (var arg in Args) Parameters.Add(arg);
      var RHS0 = DoEval(RHS).Value;
      Expression assign = LHS.Assign(RHS0, this);
      return (TDelegate)Expression.Lambda<TDelegate>(this.PopScope(assign), Parameters.Flush()).Compile();
    }
    public Expression DoSetNow00<T>(ICanAssignExpr<T, Expression> LHS, Expr<T> RHS, params IParameterExpr[] Args) {
      foreach (var arg in Args) Parameters.Add(arg);
      var RHS0 = DoEval(RHS).Value;
      Expression assign = LHS.Assign(RHS0, this);
      return (assign);
      //return Expression.Lambda<TDelegate>(this.PopScope(assign), Parameters.Flush());
    }

    public TDelegate DoSetNow<T, TDelegate>(Func<Expression,EVAL,Expression> Assign, Expr<T> RHS, params IParameterExpr[] Args) {
      foreach (var arg in Args) Parameters.Add(arg);
      var RHS0 = DoEval(RHS).Value;
      Expression assign = Assign(RHS0, this);
      return (TDelegate)Expression.Lambda<TDelegate>(this.PopScope(assign), Parameters.Flush()).Compile();
    }
    public override Func<Info<S>, Info<T>> Operator<S, T>(Bling.Ops.Operation<S, T> op) {
      var linq = op.Op.Linq(op);
      if (linq == null) {
        if (op.Op is Ops.IMemoOperator<T>) {
          var G0 = op.AsFuncLinq0;
          if (G0 != null) {
            return (s) => {
              var se = ((MyInfo<S>)s).Value;
              return new MyInfo<T>() {
                Value = Expression.Invoke(Expression.Constant(G0), se),
              };
            };
          }
        }
        return base.Operator<S, T>(op);
      }
      return s => new MyInfo<T>() { Value = linq(((MyInfo<S>)s).Value) };
    }
    public override Func<Info<S>, Info<T>, Info<U>> Operator<S, T, U>(Bling.Ops.Operation<S, T, U> op) {
      var linq = op.Op.Linq(op);
      if (linq == null) {
        if (op.Op is Ops.IMemoOperator<U>) {
          var G0 = op.AsFuncLinq0;
          if (G0 != null) {
            return (s, t) => {
              var se = ((MyInfo<S>)s).Value;
              var te = ((MyInfo<T>)t).Value;
              return new MyInfo<U>() {
                Value = Expression.Invoke(Expression.Constant(G0), se, te),
              };
            };
          }
        }
        return base.Operator<S, T, U>(op);
      }
      return (s, t) => new MyInfo<U>() { Value = linq(((MyInfo<S>)s).Value, ((MyInfo<T>)t).Value) };
    }

    public override Func<Info<S>, Info<T>, Info<U>, Info<V>> Operator<S, T, U, V>(Bling.Ops.Operation<S, T, U, V> op) {
      var linq = op.Op.Linq(op);
      if (linq == null) {
        if (op.Op is Ops.IMemoOperator<V>) {
          var G = op.AsFuncLinq;
          if (G != null) {
            return (s, t, u) => new MyInfo<V>() {
              Value = Expression.Invoke(
                Expression.Constant(G), ((MyInfo<S>)s).Value, ((MyInfo<T>)t).Value, ((MyInfo<U>)u).Value),
            };
          }
        }
        return base.Operator<S, T, U, V>(op);
      }
      return (s, t, u) => new MyInfo<V>() { Value = linq(((MyInfo<S>)s).Value, ((MyInfo<T>)t).Value, ((MyInfo<U>)u).Value) };
    }
    public override Func<Info<S>, Info<T>, Info<U>, Info<V>, Info<W>> Operator<S, T, U, V, W>(Bling.Ops.Operation<S, T, U, V, W> op) {
      var linq = op.Op.Linq(op);
      if (linq == null) {
        if (op.Op is Ops.IMemoOperator<W>) {
          var G = op.AsFuncLinq;
          if (G != null) {
            return (s, t, u, v) => new MyInfo<W>() {
              Value = Expression.Invoke(
                Expression.Constant(G), ((MyInfo<S>)s).Value, ((MyInfo<T>)t).Value, ((MyInfo<U>)u).Value, ((MyInfo<V>)v).Value),
            };
          }
        }
        return base.Operator<S, T, U, V, W>(op);
      }
      return (s, t, u, v) => new MyInfo<W>() { Value = linq(((MyInfo<S>)s).Value, ((MyInfo<T>)t).Value, ((MyInfo<U>)u).Value, ((MyInfo<V>)v).Value) };
    }
    public override Info<T> Constant<T>(Constant<T> constant) {
      if (typeof(T) == typeof(IVec<,>))
        (constant.Value != null).Assert();
      Type t = TransformType(typeof(T));
      Expression Value = Expression.Constant(constant.Value);
      Value = Convert(Value, t);
      return new MyInfo<T>() {
        Value = Value,
      };
    }
    public virtual Eval<EVAL>.Info<T> Table<T>(Expr<T>[] Values, Expr<int> Index) {
      var vT = TransformType(typeof(T));
      var idx = DoEval(Index).Value;
      var temp = Expression.Parameter(TransformType(typeof(T)), "q" + Values.GetHashCode());
      var cases = new SwitchCase[Values.Length];
      for (int i = 0; i < Values.Length; i++) {
        PushScope();
        var e = DoEval(Values[i]).Value;
        cases[i] = 
          Expression.SwitchCase(PopScope(Expression.Assign(temp, e)), Expression.Constant(i, TransformType(typeof(int))));
      }
      Expression defaultBody = ExceptionAs(vT, new InvalidOperationException("Cannot match case"));
      //Expression defaultBody = Expression.Constant(default(T), vT);
      var switchBody = Expression.Switch(idx, Expression.Assign(temp, defaultBody), cases);
      var block = Expression.Block(vT, new ParameterExpression[] { temp }, switchBody, temp);
      return new MyInfo<T>() { Value = block };
    }
    public virtual Info<T> Table<T>(T[] Values, Expr<int> Index) {
      var idx = DoEval(Index).Value;

      Expression access = Expression.ArrayAccess(Expression.Constant(Values), idx);
      access = Convert(access, TransformType(typeof(T)));
      return new MyInfo<T>() { Value = access };
    }
    public Info<T> Block<T>(T[] Values, Info<int> idx) {
      var idx0 = ((MyInfo<int>)idx).Value;
      if (idx0 is ConstantExpression) {
        var idx1 = (int)((ConstantExpression)idx0).Value;
        return new MyInfo<T>() { Value = Expression.Constant(Values[idx1], TransformType(typeof(T))) };
      } else return new MyInfo<T>() {
        Value = Expression.ArrayAccess(Expression.Constant(Values, TransformType(typeof(T).MakeArrayType())), idx0),
      };
    }
    private Expression PopScope(Expression e, Func<ParameterExpression[], IList<Expression>, Expression> F) {
      var temps = base.PopScope();
      if (temps.Count == 0) return e;
      var tempDefs = new ParameterExpression[temps.Count];
      var Instructions = new List<Expression>();
      for (int i = 0; i < temps.Count; i++) {
        tempDefs[i] = (ParameterExpression)((MyInfo)temps[i].AsTemp).Value;
        Instructions.Add(Expression.Assign(tempDefs[i], ((MyInfo)temps[i].Code).Value));
      }
      Instructions.Add(e);
      return F(tempDefs, Instructions);
    }
    public Expression PopScope(Expression e) {
      return PopScope(e, (tempDefs, Instructions) => Expression.Block(tempDefs, Instructions));
    }
    public Expression PopScope<T>(Expression e) {
      return PopScope(e, (tempDefs, Instructions) => Expression.Block(TransformType(typeof(T)), tempDefs, Instructions));
    }
    protected override Info<S> AllocateTemp<S>(Scope scope, Info<S> Code) {
      var name = "t" + scope.Depth + "_" + scope.Order.Count;
      return new MyInfo<S> { Value = Expression.Parameter(TransformType(typeof(S)), name) };
    }
    public Info<S> Condition<S>(Expr<bool> Test, Expr<S> IfTrue, Expr<S> IfFalse) {
      MyInfo<bool> TestE = DoEval(Test);
      MyInfo<S> IfTrueD, IfFalseD;
      {
        Expression IfTrueE, IfFalseE;
        {
          PushScope();
          IfTrueD = DoEval(IfTrue);
          IfTrueE = IfTrueD.Value;
          IfTrueE = PopScope<S>(IfTrueE);
        }
        {
          PushScope();
          IfFalseD = DoEval(IfFalse);
          IfFalseE = IfFalseD.Value;
          IfFalseE = PopScope<S>(IfFalseE);
        }
        var ret = new MyInfo<S>() {
          Value = Expression.Condition(TestE.Value, IfTrueE, IfFalseE),
        };
        this.AtLeast(ret, TestE);
        this.AtLeast(ret, IfTrueD);
        this.AtLeast(ret, IfFalseD);
        return ret;
      }
    }
    public interface MyInfo : Info {
      Expression Value { get; }
    }
    public new MyInfo DoEval(Expr Expr) { return (MyInfo)base.DoEval(Expr); }
    public new MyInfo<T> DoEval<T>(Expr<T> Expr) { return (MyInfo<T>)base.DoEval(Expr); }
    public class MyInfo<T> : Info<T>, MyInfo {
      public Expression Value { get; set; }
    }

    protected LambdaExpression Eval0(Expression result) {
      var body = PopScope(result);
      var lambda0 = Expression.Lambda(body, Parameters.Flush());
      return lambda0;
    }


    // convenience.
    protected Expression<TDelegate> Eval0<TDelegate>(Expression result) {
      var lambda0 = Eval0(result);
      lambda0 = (LambdaExpression) Convert(lambda0, typeof(TDelegate));
      return (Expression<TDelegate>) lambda0;
    }
    protected TDelegate Eval0<T, TDelegate>(Expr<T> value) {
      var result = DoEval(value).Value;
      return Eval<TDelegate>((result));
    }

    public Expression Convert(Expression e, Type type) {
      if (e.Type == type) return e;
      if (e.Type == typeof(bool) || type == typeof(bool)) {
        true.Assert();
      }
      if (type.IsGenericType && e.Type.IsGenericType && 
          type.GetGenericTypeDefinition() == e.Type.GetGenericTypeDefinition() && type.BaseType == (typeof(System.MulticastDelegate))) {

        var Invoke = type.GetMethod("Invoke");

        //var le = (LambdaExpression)e;
        var generic = type.GetGenericTypeDefinition();
        var ArgCount = Invoke.GetParameters().Length;
        var TotalCount = ArgCount + (Invoke.ReturnType == typeof(void) ? 0 : 1);
        (TotalCount == type.GetGenericArguments().Length).Assert();

        var Ps = new ParameterExpression[ArgCount];
        var Es = new Expression[Ps.Length];
        for (int i = 0; i < Ps.Length; i++) {
          Ps[i] = Expression.Parameter(type.GetGenericArguments()[i], "p" + i);
          Es[i] = Convert(Ps[i], e.Type.GetGenericArguments()[i]);
        }
        Expression invoke = Expression.Invoke(e, Es);
        if (ArgCount == TotalCount) { } 
        else invoke = Convert(invoke, type.GetGenericArguments()[Es.Length]);

        return Expression.Lambda(invoke, Ps);
      } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IVec<,>)) {
        var K = type.GetGenericArguments()[0];
        var D = type.GetGenericArguments()[1];
        if (D == typeof(D1) && e.Type == K) {
          // create a vec1, 
          var ToType = typeof(Vec1<>).MakeGenericType(K);
          var p = Expression.Parameter(ToType, "Vec0");

          var block = Expression.Block(type, new ParameterExpression[] { p },
            Expression.Assign(p, Expression.New(ToType)),
            Expression.Assign(Expression.Field(p, "_0"), e),
            Expression.Convert(p, type)
          );
          e = block;
        } else {
          e = Expression.Convert(e, type);
        }
        return e;
      } else if (e.Type.IsGenericType && e.Type.GetGenericTypeDefinition() == typeof(IVec<,>)) {
        var K = e.Type.GetGenericArguments()[0];
        var D = e.Type.GetGenericArguments()[1];
        if (D == typeof(D1) && type == K) { // the other way.
          e = Expression.Convert(e, typeof(Vec1<>).MakeGenericType(K));
          e = Expression.Field(e, "_0");
        } else {
          e = Expression.Convert(e, type);
        }
        return e;
      } else if (e.Type.IsGenericType && e.Type.GetGenericTypeDefinition() == typeof(Vec1<>)) {
        (e.Type.GetGenericArguments()[0] == type).Assert();
        e = Expression.Field(e, "_0");
        return e;
      } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Func<>)) {
        (e.Type.IsGenericType).Assert();
        (e.Type.GetGenericTypeDefinition() == typeof(Microsoft.Func<>)).Assert();
        var result = Expression.Invoke(e, new Expression[] { });
        var result1 = Convert(result, type.GetGenericArguments()[0]);
        return Expression.Lambda(type, result1, new ParameterExpression[] {} );
      } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Func<,>)) {
        (e.Type.IsGenericType).Assert();
        (e.Type.GetGenericTypeDefinition() == typeof(Microsoft.Func<,>)).Assert();

        var p0 = Expression.Parameter(type.GetGenericArguments()[0], "p0");
        var q0 = Convert(p0, e.Type.GetGenericArguments()[0]);

   
        var result = Expression.Invoke(e, new Expression[] { q0, });
        var result1 = Convert(result, type.GetGenericArguments()[1]);
        return Expression.Lambda(type, result1, p0);
      } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Func<,,>)) {
        (e.Type.IsGenericType).Assert();
        (e.Type.GetGenericTypeDefinition() == typeof(Microsoft.Func<,,>)).Assert();

        var p0 = Expression.Parameter(type.GetGenericArguments()[0], "p0");
        var p1 = Expression.Parameter(type.GetGenericArguments()[1], "p1");
        var q0 = Convert(p0, e.Type.GetGenericArguments()[0]);
        var q1 = Convert(p1, e.Type.GetGenericArguments()[1]);


        var result = Expression.Invoke(e, new Expression[] { q0, q1, });
        var result1 = Convert(result, type.GetGenericArguments()[2]);
        return Expression.Lambda(type, result1, p0, p1  );
      } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Func<,,,>)) {
        (e.Type.IsGenericType).Assert();
        (e.Type.GetGenericTypeDefinition() == typeof(Microsoft.Func<,,,>)).Assert();

        var p0 = Expression.Parameter(type.GetGenericArguments()[0], "p0");
        var p1 = Expression.Parameter(type.GetGenericArguments()[1], "p1");
        var p2 = Expression.Parameter(type.GetGenericArguments()[2], "p2");
        var q0 = Convert(p0, e.Type.GetGenericArguments()[0]);
        var q1 = Convert(p1, e.Type.GetGenericArguments()[1]);
        var q2 = Convert(p2, e.Type.GetGenericArguments()[2]);

        var result = Expression.Invoke(e, new Expression[] { q0, q1, q2 });
        var result1 = Convert(result, type.GetGenericArguments()[3]);
        return Expression.Lambda(type, result1, p0, p1, p2);
      } else {
        if (type.IsGenericType != e.Type.IsGenericType)
          true.Assert();
        try {
          return Expression.Convert(e, type);
        } catch (Exception ee) {
          throw ee;
        }
      }
    }


    public TDelegate Eval<TDelegate>(Expression result) {
      var lambda = Eval0<TDelegate>(result);
      (lambda.Type == typeof(TDelegate)).Assert();
      return lambda.Compile();
    }
    public Func<T> Eval<T>(Expr<T> value) {
      var f = Eval0<T, System.Func<T>>(value);
      return () => f();
    }
    public System.Func<S, T> Eval<S, T>(Func<Expr<S>, Expr<T>> value) {
      var slots = Parameters;
      var slot = slots.NewParameter<S>();
      var f = Eval0<T, System.Func<S, T>>(value(slot));
      return f;
    }
    public Expression Eval000<S, T>(Func<Expr<S>, Expr<T>> value) {
      var slots = Parameters;
      var slot = slots.NewParameter<S>();
      var result = DoEval(value(slot)).Value;
      return this.PopScope(result);
    }
    public System.Func<S, T, U> Eval<S, T, U>(Func<Expr<S>, Expr<T>, Expr<U>> value) {
      var slots = Parameters;
      var slot1 = slots.NewParameter<S>();
      var slot2 = slots.NewParameter<T>();
      var f = Eval0<U, System.Func<S, T, U>>(value(slot1, slot2));
      return f;
      //return (e1, e2) => f(e1, e2);
    }
    public LambdaExpression Eval00<S, T, U>(Func<Expr<S>, Expr<T>, Expr<U>> value) {
      var slots = Parameters;
      var slot1 = slots.NewParameter<S>();
      var slot2 = slots.NewParameter<T>();
      var result = DoEval(value(slot1, slot2)).Value;
      return Eval0(result);


      //var f = Eval0<U, System.Func<S, T, U>>(value(slot1, slot2));
      //return f;
      //return (e1, e2) => f(e1, e2);
    }

    public LambdaExpression Eval00<S, T>(Func<Expr<S>, Expr<T>> value) {
      var slots = Parameters;
      var slot1 = slots.NewParameter<S>();
      var result = DoEval(value(slot1)).Value;
      return Eval0(result);


      //var f = Eval0<U, System.Func<S, T, U>>(value(slot1, slot2));
      //return f;
      //return (e1, e2) => f(e1, e2);
    }



    public System.Func<S, T, U, V> Eval<S, T, U, V>(Func<Expr<S>, Expr<T>, Expr<U>, Expr<V>> value) {
      var slots = Parameters;
      var slot1 = slots.NewParameter<S>();
      var slot2 = slots.NewParameter<T>();
      var slot3 = slots.NewParameter<U>();
      return Eval0<V, System.Func<S, T, U, V>>(value(slot1, slot2, slot3));
    }
    public Func<S, T, U, V, W> Eval<S, T, U, V, W>(Func<Expr<S>, Expr<T>, Expr<U>, Expr<V>, Expr<W>> value) {
      var slots = Parameters;
      var slot1 = slots.NewParameter<S>();
      var slot2 = slots.NewParameter<T>();
      var slot3 = slots.NewParameter<U>();
      var slot4 = slots.NewParameter<V>();
      return Eval0<W, Func<S, T, U, V, W>>(value(slot1, slot2, slot3, slot4));
    }

    public Expression MakeLoopAssign0<BRAND>(IntBl count,
      Func<IntBl, BRAND> LHS, Func<IntBl, BRAND> RHS) where BRAND : Brand<BRAND> {
      var loopV = Expression.Parameter(typeof(int), "I");
      var loopVar = new LinqExpr<int>(loopV);
      var LHS0 = LHS(loopVar);
      var RHS0 = RHS(loopVar);
      // figure this out later.
      throw new NotSupportedException();
    }

    public Expression MakeLoopAssign<T>(
      int count, string nm, 
      Func<Expr<int>, ICanAssignExpr<T,Expression>> LHS,
      Func<Expr<int>, Expr<T>> RHS) {
      return MakeMultiLoopAssign<T>(new int[] { count }, new string[] { nm },
        es => LHS(es[0]), es => RHS(es[0]));
    }
    public Expression MakeMultiLoopAssign<T>(
      int[] count, string[] nm,
      Func<Expr<int>[], ICanAssignExpr<T,Expression>> LHS,
      Func<Expr<int>[], Expr<T>> RHS) {
      var loopVarsA = new ParameterExpression[count.Length];
      var loopVars = new LinqExpr<int>[count.Length];

      for (int i = 0; i < loopVarsA.Length; i++) {
        loopVarsA[i] = Expression.Parameter(typeof(int), nm[i]);
        loopVars[i] = new LinqExpr<int>(loopVarsA[i]);
      }
      var RHS1 = RHS(loopVars);
      var LHS1 = LHS(loopVars);
      // do the last one first.
      var lastIndex = count.Length - 1;
      (lastIndex >= 0).Assert();
      Expression body;
      {
        PushScope();
        var RHS0 = DoEval(RHS1).Value;
        body = LHS1.Assign(RHS0, this);
        body = PopScope(body);
      }
      while (lastIndex >= 0) {
        // convert to loop.
        var updateVar = Expression.Assign(loopVarsA[lastIndex], Expression.Add(loopVarsA[lastIndex], Expression.Constant(1, typeof(int))));
        var breakLabel = Expression.Label("BREAK_" + nm[lastIndex]);
        var checkBreak = Expression.IfThen(Expression.Equal(loopVarsA[lastIndex], Expression.Constant(count[lastIndex], typeof(int))), 
          Expression.Break(breakLabel));
        var loop = Expression.Loop(Expression.Block(body, updateVar, checkBreak), breakLabel);
        body = Expression.Block(new ParameterExpression[] { loopVarsA[lastIndex] },
            Expression.Assign(loopVarsA[lastIndex], Expression.Constant(0, typeof(int))),
            loop);
        lastIndex -= 1;
      }
      return body;
    }
    public Expression End(Expression e) {
      (Top.Depth == 0).Assert();
      return PopScope(e);
    }

    public Expression DoIf(Expression Test, Func<EVAL,Expression> IfTrue, Func<EVAL,Expression> IfFalse) {
      Expression IfTrue0, IfFalse0;
      PushScope();
      IfTrue0 = PopScope(IfTrue(this));
      PushScope();
      IfFalse0 = PopScope(IfFalse(this));
      return Expression.IfThenElse(Test, IfTrue0, IfFalse0);

      //return (Expression.Condition(Test, IfTrue0, IfFalse0));
    }
    public Expression Exception {
      get {
        var e = Expression.New(typeof(NotSupportedException));
        return Expression.Throw(e);
      }
    }

      public static T MakeE<T>(Exception ee) {
        throw ee;
      }

    public Expression ExceptionAs(Type t, Exception ee) {
      //if (true) return Expression.Throw(Expression.Constant(ee), t);
      var mthd = (typeof(LinqEval<EVAL>)).GetMethod("MakeE", new Type[] { typeof(Exception) });
      mthd = mthd.MakeGenericMethod(t);
      return Expression.Call(mthd, Expression.Constant(ee));
    }

    public Expression Block(IList<Expression> Expressions) {
      if (Expressions.Count == 0) return Expression.Empty();
      else if (Expressions.Count == 1) return Expressions[0];
      else return Expression.Block(Expressions);
    }

    public Expression DoLoop(LoopIndexExpr LoopIndex, Func<EVAL, Expression> Body) {
      var loopVar = Expression.Parameter(TransformType(typeof(int)), LoopIndex.Name);
      return DoLoop(LoopIndex, loopVar, Body);
    }
    public Expression DoLoop(LoopIndexExpr LoopIndex, ParameterExpression loopVar, Func<EVAL, Expression> Body) {
      var count = DoEval(LoopIndex.Count).Value;
      PushScope();
      PutParamInScope(LoopIndex, new MyInfo<int>() { Value = loopVar });
      var body = Body((EVAL)(object)this);
      body = PopScope(body);
      Expression loop;
      {
        var updateVar = Expression.Assign(loopVar, Expression.Add(loopVar, Expression.Constant(1)));
        var breakLabel = Expression.Label("BREAK_" + LoopIndex.Name);
        var checkBreak = Expression.IfThen(Expression.Equal(loopVar, count), Expression.Break(breakLabel));
        loop = Expression.Loop(Expression.Block(body, updateVar, checkBreak), breakLabel);
      }
      {
        var initVar = Expression.Assign(loopVar, Expression.Constant(0));
        loop = Expression.Block(new ParameterExpression[] { loopVar }, initVar, loop);
      }
      return loop;
    }
    public Expression MakeAssign<T>(Expr<T> LHS, Expr<T> RHS) {
      if (LHS is ICanAssignExpr<T, Expression>) {
        return ((ICanAssignExpr<T, Expression>)LHS).Assign(Extract(DoEval(RHS)), this);
      } else if (LHS is NoAssignExpr<T>) return Expression.Empty();
      else throw new NotSupportedException();
    }
    public Expression MakeAssign(Expr LHS, Expr RHS) {
      if (LHS is ICanAssignExpr<Expression>) {
        return ((ICanAssignExpr<Expression>)LHS).Assign(Extract(DoEval(RHS)), this);
      } else if (LHS is INoAssignExpr) return Expression.Empty();
      else throw new NotSupportedException();
    }

    public Expression Extract<T>(Info<T> Info) { return ((MyInfo<T>)Info).Value; }
    public Expression Extract(Info Info) { return ((MyInfo)Info).Value; }

    private class TryLoopEval : TranslateEval<TryLoopEval> {
      public readonly HashSet<LoopIndexExpr> Loops = new HashSet<LoopIndexExpr>();
      protected override Expr<T> RealTranslate<T>(Expr<T> value) {
        if (value is LoopIndexExpr) Loops.Add((LoopIndexExpr)(object)value);
        return base.RealTranslate<T>(value);
      }
    }
    private class TryLoopEval2 : TranslateEval<TryLoopEval2> {
      public LoopIndexExpr OldIndex;
      public Expr<int> NewIndex;
      protected override Expr<T> RealTranslate<T>(Expr<T> value) {
        if (value.Equals(OldIndex)) return (Expr<T>)(object)NewIndex;
        else return base.RealTranslate<T>(value);
      }
    }

    public Expression TryLoop(Expr<int> Index, Expr RHS, Func<Expression, Expression, Expression> F) {
      var findLoops = new TryLoopEval();
      findLoops.Translate(Index);
      if (findLoops.Loops.Count == 0) { // nothing to do, just evaluate directly

        PushScope();
        var index0 = DoEval(Index).Value;
        index0 = PopScope(index0);

        PushScope();
        var RHS0 = DoEval(RHS).Value;
        RHS0 = PopScope(RHS0);

        return F(index0, RHS0);
      } else if (findLoops.Loops.Count == 1) {
        var e = findLoops.Loops.GetEnumerator();
        e.MoveNext();
        var loop = e.Current;
        return For(loop.Name, loop.Count, i => {
          LinqExpr<int> NewIndex = new LinqExpr<int>(i.Value);
          var translate = new TryLoopEval2() { OldIndex = loop, NewIndex = NewIndex };
          var Index1 = translate.Translate(Index);
          var RHS1 = translate.Translate(RHS);
          
          PushScope();
          var index0 = DoEval(Index1).Value;
          index0 = PopScope(index0);

          PushScope();
          var RHS0 = DoEval(RHS1).Value;
          RHS0 = PopScope(RHS0);

          return F(index0, RHS0);
        });
      }
      throw new NotSupportedException();
    }
    public Expression For(string nm, Expr<int> count, Func<MyInfo<int>, Expression> Body) {
      var loopVar = Expression.Parameter(typeof(int), nm);
      
      PushScope();
      var body = Body(new MyInfo<int>() { Value = loopVar });
      body = PopScope(body);

      PushScope();
      var count0 = DoEval(count).Value;
      count0 = PopScope(count0);

      var updateVar = Expression.Assign(loopVar, Expression.Add(loopVar, Expression.Constant(1)));
      var breakLabel = Expression.Label("BREAK_" + nm);
      var checkBreak = Expression.IfThen(Expression.Equal(loopVar, count0), Expression.Break(breakLabel));
      var loop = Expression.Loop(Expression.Block(body, updateVar, checkBreak), breakLabel);
      return Expression.Block(new ParameterExpression[] { loopVar },
          Expression.Assign(loopVar, Expression.Constant(0)),
          loop);
    }
    public Expression For(LoopIndexExpr i, Func<ParameterExpression,Expression> Body) {
      var loopVar = Expression.Parameter(typeof(int), i.Name);

      Expression body;
      {
        (!this.Parameters.Slots.Contains(i)).Assert();
        this.Parameters[i] = loopVar;
        PushScope();
        body = Body(loopVar);
        body = PopScope(body);
        this.Parameters.Slots.Remove(i);
      }
      Expression count0;
      {
        PushScope();
        count0 = DoEval(i.Count).Value;
        count0 = PopScope(count0);
      }
      var updateVar = Expression.Assign(loopVar, Expression.Add(loopVar, Expression.Constant(1)));
      var breakLabel = Expression.Label("BREAK_" + i.Name);
      var checkBreak = Expression.IfThen(Expression.Equal(loopVar, count0), Expression.Break(breakLabel));
      var loop = Expression.Loop(Expression.Block(body, updateVar, checkBreak), breakLabel);
      return Expression.Block(new ParameterExpression[] { loopVar },
          Expression.Assign(loopVar, Expression.Constant(0)),
          loop);


    }

  }
  public partial class DefaultLinqEval : LinqEval<DefaultLinqEval> {}
}
